feat(Tabs): add native RTL support for bottom tabs on iOS & Android#3613
feat(Tabs): add native RTL support for bottom tabs on iOS & Android#3613ahmedawaad1804 wants to merge 13 commits intosoftware-mansion:mainfrom
Conversation
…ntroller mode handling
|
Hey, thanks for the PR. This is great, we need it. I hope to review it somewhere this week. Will keep you up to date. |
|
Hi, thank you for the PR. I'll push some changes and update PR description so we can land it soon. I hope you don't mind. |
|
@kligarski sure thank you :D |
|
@kkafar welcome , any time :) |
kligarski
left a comment
There was a problem hiding this comment.
Leaving some comments for the reviewers.
| } | ||
|
|
||
| if (newComponentProps.directionMode != oldComponentProps.directionMode) { | ||
| _directionMode = |
There was a problem hiding this comment.
We can use [RCTI18nUtil isRTL] instead of passing a prop, it should have an updated value as it reads it from [NSUserDefaults standardUserDefaults] (but it would be unable to react to dynamic changes). I'm not sure if we can consider this a stable API but it has been mentioned in blog post in react-native: https://reactnative.dev/blog/2016/08/19/right-to-left-support-for-react-native-apps.
cc @kkafar - let me know what you think is better
We can also leave the prop for now (it's not exposed as a part of API either way) and rethink our approach to RTL in separate PR - I'm wondering whether RTL should be handled via some top-level wrapper component for the entire hierarchy below it.
There was a problem hiding this comment.
@kligarski in react navigation, we have a direction prop on NavigationContainer to control layout direction which we will pass it to navigators that can handle it.
t0maboro
left a comment
There was a problem hiding this comment.
leaving 1 comment, I haven't verified the runtime yet
| #if RNS_IPHONE_OS_VERSION_AVAILABLE(17_0) | ||
| if (@available(iOS 17.0, *)) { | ||
| _controller.traitOverrides.layoutDirection = _directionMode == UISemanticContentAttributeForceRightToLeft | ||
| ? UITraitEnvironmentLayoutDirectionRightToLeft | ||
| : UITraitEnvironmentLayoutDirectionLeftToRight; | ||
| } else | ||
| #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(17_0) | ||
| { | ||
| _controller.view.semanticContentAttribute = _directionMode; | ||
| _controller.tabBar.semanticContentAttribute = _directionMode; | ||
| [[UIView appearanceWhenContainedInInstancesOfClasses:@[ _controller.tabBar.class ]] | ||
| setSemanticContentAttribute:_directionMode]; | ||
| } | ||
| } |
There was a problem hiding this comment.
This is repeated from updateProps, can be deduplicated with a helper method
Description
This PR adds proper RTL (Right-to-Left) layout support for native bottom tabs on both iOS and Android in react-native-screens.
Previously, bottom tabs did not automatically follow the system RTL direction, which caused incorrect tab ordering and layout in RTL locales (e.g. Arabic, Hebrew).
With this change bottom tabs now follow the system layout direction by default. This brings native behavior in line with expected platform RTL handling and React Native layout conventions.
Details
@kligarski:
Android
On Android, the direction works out-of-the-box so no custom logic is necessary.
iOS
Badges
Setting
semanticContentAttributefor_controller.tabBarand_controller.viewis not enough, e.g. badges visible through liquid glass lens are still in LTR.Simulator.Screen.Recording.-.iPhone.17.Pro.RTL.-.2026-02-19.at.16.27.18.mov
To handle this, we're using the same approach as in native stack - we set
UIView'sappearanceWhenContainedInInstancesOfClassesof the tab bar (details how it's handled in the header are here).However, this does not work for tab bar & sidebar on iPad starting from iOS 18 as it is not a part of
controller.tabBar._UITabContentViewis mounted undercontroller.view. UsingappearanceWhenContainedInInstancesOfClassesfor_UITabContentView(which is already sketchy as this is an internal UIKit class) helps with the order of items in the tab bar but the sidebar appears on the wrong side of the screen.That's why I decided to use modern way to handle direction via trait overrides. For iOS prior to 17, you need to apply overrides on parent view controller (see here), that's why I decided to leave current solution with
appearanceWhenContainedInInstancesOfClassesfor those versions instead of trait override. There is no iPad top tab bar/sidebar/bottom accessory in these versions of iOS so the solution should be enough.For iOS 17+, we can use
traitOverridesdirectly on the controller - this works with the top tab bar/sidebar on iPadOS 18+.ScrollView
On iOS, there seems to be a very odd bug with content of the ScrollView being moved off screen after tab changes. I've created an issue on our internal board (https://github.com/software-mansion/react-native-screens-labs/issues/985) and we'll investigate this further.
Screen.Recording.2026-02-19.at.15.55.06.mov
Bottom Accessory
There seems to be a bug with bottom accessory in RTL when search role is NOT used for one of the tabs (Apple Music and Apple Podcasts use search role so the bug isn't visible).
Simulator.Screen.Recording.-.iPhone.17.Pro.RTL.-.2026-02-19.at.15.52.15.mov
Simulator.Screen.Recording.-.iPhone.17.Pro.RTL.-.2026-02-19.at.15.51.31.mov
This bug is reproducible in bare UIKit app on iOS 26.2.
Simulator.Screen.Recording.-.iPhone.17.Pro.RTL.-.2026-02-19.at.16.19.15.mov
I've added this to our internal board (https://github.com/software-mansion/react-native-screens-labs/issues/986) and we'll check whether it has been fixed in iOS 26.3/26.4 beta.
Top tab bar badges
On iOS 18+, there seems to be a bug with the initial position of the badges. They move to correct position after a tab change. I added a ticket on our internal board to check whether this is a native bug: https://github.com/software-mansion/react-native-screens-labs/issues/991.
Simulator.Screen.Recording.-.iPad.Pro.13-inch.M5.-.2026-02-23.at.09.47.57.mov
Changes
Test3598.tsxBefore
(iOS only because Android works out of the box).

After
Android
android_3613.mp4
iOS
Simulator.Screen.Recording.-.iPhone.17.Pro.RTL.-.2026-02-19.at.15.49.45.mov
Simulator.Screen.Recording.-.iPhone.17.Pro.RTL.-.2026-02-19.at.15.50.11.mov
Simulator.Screen.Recording.-.iPhone.17.Pro.RTL.-.2026-02-19.at.15.51.31.mov
Test plan
Use
Test3598,TestBottomTabs,Test3288(iOS).Tested on:
Checklist